using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using LTreesLibrary.Trees;
using LTreesLibrary.Trees.Wind;


namespace Advanced_Terrain
{
    public struct VertexMultitextured
    {

        public Vector3 Position;
        public Vector3 Normal;
        public Vector4 TextureCoordinate;
        public Vector4 TexWeights;
        public static int SizeInBytes = (3 + 3 + 4 + 4) * sizeof(float);
        public static VertexElement[] VertexElements = new VertexElement[]
          {
              new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
              new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
              new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ),
              new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ),
          };
    }

    
    public class Terrain : DrawableGameComponent
    {
        
        public float waterHeight = 5.0f;
        public const int numTrees = 10;
        public int terrainWidth;
        public int terrainLength;
        public static float[,] heightData;
        public Vector3 windDirection = new Vector3(0, 0, 1);
        public static Boolean TreesExist = true;
        public List<TreeClass> TreeClassList;
        
        GraphicsDevice device;
        TreeProfile[] Treeprofile = new TreeProfile[4];
        SimpleTree[] AvailablePineTrees = new SimpleTree[10];
        SimpleTree[] AvailableBirchTrees = new SimpleTree[10];
        SimpleTree[] AvailableGardenwoodTrees = new SimpleTree[10];
        SimpleTree[] AvailableWillowTrees = new SimpleTree[10];
        SimpleTree[] AvailablePineTreesLowPoly = new SimpleTree[10];
        SimpleTree[] AvailableBirchTreesLowPoly = new SimpleTree[10];
        SimpleTree[] AvailableGardenwoodTreesLowPoly = new SimpleTree[10];
        SimpleTree[] AvailableWillowTreesLowPoly = new SimpleTree[10];
 
        TreeWindAnimator animator;
        WindStrengthSin wind;
        ContentManager Content;
        VertexBuffer terrainVertexBuffer;
        IndexBuffer terrainIndexBuffer;
        VertexDeclaration terrainVertexDeclaration;
        VertexBuffer waterVertexBuffer;
        VertexDeclaration waterVertexDeclaration;
        VertexBuffer treeVertexBuffer;
        VertexDeclaration treeVertexDeclaration;
        Effect effect;
        Effect bbEffect;
        RenderTarget2D refractionRenderTarget;
        Texture2D refractionMap;
        RenderTarget2D reflectionRenderTarget;
        Texture2D reflectionMap;
        RenderTarget2D cloudsRenderTarget;
        Texture2D cloudStaticMap;
        VertexPositionTexture[] fullScreenVertices;
        VertexDeclaration fullScreenVertexDeclaration;
        Texture2D grassTexture;
        Texture2D sandTexture;
        Texture2D rockTexture;
        Texture2D snowTexture;
        Texture2D cloudMap;
        Texture2D waterBumpMap;
        Texture2D treeTexture;
        //Texture2D treeMap;
        Model skyDome;
        Matrix TreeScaleMatrix = Matrix.CreateScale(.0032f);
        //Game Game;
        

        public static List<Vector3> treeList;

        public Terrain(Game game, ContentManager content, GraphicsDevice Device)
            : base(game)
        {
           Content = content;
           device = Device;
           //Game = game;
           this.Visible = false;
        }

        
        public override void Initialize()
        {
            wind = new WindStrengthSin();
            animator = new TreeWindAnimator(wind);
            Treeprofile[0] = Content.Load<TreeProfile>("Trees/Willow");
            Treeprofile[1] = Content.Load<TreeProfile>("Trees/Pine");
            Treeprofile[2] = Content.Load<TreeProfile>("Trees/Birch");
            Treeprofile[3] = Content.Load<TreeProfile>("Trees/Gardenwood");
            LoadTextures();
            base.Initialize();
        }
        protected override void LoadContent()
        {
            LoadTextures();
            effect = Content.Load<Effect>("AdvTerrainEffects");
            bbEffect = Content.Load<Effect>("bbEffect");
            skyDome = Content.Load<Model>("dome"); 
            skyDome.Meshes[0].MeshParts[0].Effect = effect.Clone(device);
            PresentationParameters pp = device.PresentationParameters;
            refractionRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, device.DisplayMode.Format);
            reflectionRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, device.DisplayMode.Format);
            cloudsRenderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, device.DisplayMode.Format);

            base.LoadContent();
        }

       
        public override void Update(GameTime gameTime)
        {
            switch (Game1.GameState)
            {

                case Game1.GameStatePreAiming:
                    {
                        break;
                    }
                    
                    
                case Game1.GameStateAiming:
                    {
                        wind.Update(gameTime);
                        Random TreeTest = new Random();
                        //foreach (TreeClass tempTree in TreeClassList)
                        //{
                        //System.Diagnostics.Debug.Print(tempTree.Position.X.ToString() + "  " + tempTree.Position.Z.ToString());
                        //if (tempTree.TreeType == 1)
                        //{
                        //     AvailablePineTrees[tempTree.Tree].DrawTrunk(Matrix.CreateScale(.001f) * Matrix.CreateTranslation(tempTree.Position), viewMatrix, CameraProperties.projectionMatrix);
                        //     animator.WindSource = wind;
                        //     animator.Animate(AvailablePineTrees[tempTree.Tree].Skeleton, AvailablePineTrees[tempTree.Tree].AnimationState, gameTime);
                        // }

                        //}
                        /*foreach (TreeClass tempTree in TreeClassList)
                        {
                            switch (tempTree.TreeType)
                            {
                                case 0:
                                    animator.Animate(AvailableWillowTrees[tempTree.Tree].Skeleton, AvailableWillowTrees[tempTree.Tree].AnimationState, gameTime);
                                    break;
                                case 1:
                                    animator.Animate(AvailablePineTrees[tempTree.Tree].Skeleton,AvailablePineTrees[tempTree.Tree].AnimationState,gameTime);

                                    break;
                                case 2:
                                    animator.Animate(AvailableBirchTrees[tempTree.Tree].Skeleton, AvailableBirchTrees[tempTree.Tree].AnimationState, gameTime);

                                    break;
                                case 3:
                                    animator.Animate(AvailableGardenwoodTrees[tempTree.Tree].Skeleton, AvailableGardenwoodTrees[tempTree.Tree].AnimationState, gameTime);

                                    break;
                                default:
                                    break;
                            }
                        }    
                        */
                        break;
                    }
                case Game1.GameStateFlying:
                    {
                        //BasketInformation.TextLabel = "Gamestateflying" ;
                        /*
                        foreach (TreeClass tempTree in TreeClassList)
                        {
                            switch (tempTree.TreeType)
                            {
                                case 0:
                                    animator.Animate(AvailableWillowTrees[tempTree.Tree].Skeleton, AvailableWillowTrees[tempTree.Tree].AnimationState, gameTime);
                                    break;
                                case 1:
                                    animator.Animate(AvailablePineTrees[tempTree.Tree].Skeleton, AvailablePineTrees[tempTree.Tree].AnimationState, gameTime);
                                    break;
                                case 2:
                                    animator.Animate(AvailableBirchTrees[tempTree.Tree].Skeleton, AvailableBirchTrees[tempTree.Tree].AnimationState, gameTime);
                                    break;
                                case 3:
                                    animator.Animate(AvailableGardenwoodTrees[tempTree.Tree].Skeleton, AvailableGardenwoodTrees[tempTree.Tree].AnimationState, gameTime);
                                    break;
                                default:
                                    break;
                            }
                        }
                         */
                        //PlayerStatus.Position -= new Vector2(100, 0);
                        //PlayerStatus.TextLabel = "PowerChanger: " + WiiMotePowers.X.ToString() + " Angle Changer : " + MyWiimote.Angle.Z.ToString();
                        break;
                    }
                case Game1.GameStateLanded:
                    {
                        break;
                    }
                case Game1.GameStateWater:
                    {
                        break;
                    }
                case Game1.GameStateOB:
                    {
                        break;
                    }
                case Game1.GameStateMenu:
                    {
                        break;
                    }

                case Game1.GameStateInBasket:
                    {
                        break;
                    }
                case Game1.GameStateLoadHole:
                    {
                        LoadVertices();
                        LoadHole();
                        break;
                    }
                case Game1.GameStateShakingChains:
                    {
                        break;
                    }
                case Game1.GameStateZoomingToBasket:
                    break;
                case Game1.GameStateSwitchMenus:
                    break;
                default:
                    break;
            }
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            //CreateArrow(gameTime);

            if (this.Visible)
            {

                switch (Game1.GameState)
                {
                    case Game1.GameStateDisplayingScores:
                        {
                            break;

                        }
                    case Game1.GameStateLoadHole:
                        break;
                    case Game1.GameStatePreAiming:
                        {
                            //if (Delay > 1)
                            //{
                            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 300.0f;
                            GeneratePerlinNoise(time);
                            DrawRefractionMap();
                            DrawReflectionMap();
                            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
                            //DrawSkyDome(viewMatrix);
                            DrawTerrain(Game1.viewMatrix);
                            DrawWater(time);
                            //DrawBillboards(viewMatrix);
                            DrawTrees();
                            base.Draw(gameTime);
                            //}
                            break;
                        }
                    case Game1.GameStateAiming:
                        {

                            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 300.0f;
                            GeneratePerlinNoise(time);
                            DrawRefractionMap();
                            DrawReflectionMap();
                            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);

                            //DrawSkyDome(viewMatrix);
                            DrawTerrain(Game1.viewMatrix);
                            DrawWater(time);
                            DrawTrees();
                            //DrawBillboards(viewMatrix);
                            base.Draw(gameTime);

                            //Tree.DrawTrunk(Matrix.CreateScale(.001f) * Matrix.CreateTranslation(newBasket.Position), viewMatrix, CameraProperties.projectionMatrix);
                            //Tree.DrawLeaves(Matrix.CreateScale(.001f) * Matrix.CreateTranslation(newBasket.Position), viewMatrix, CameraProperties.projectionMatrix);

                            break;
                        }
                    case Game1.GameStateWater:
                        break;
                    case Game1.GameStateMenu:
                        break;

                    case Game1.GameStateVertPower:
                        {
                            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 300.0f;
                            GeneratePerlinNoise(time);
                            DrawRefractionMap();
                            DrawReflectionMap();
                            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);
                            //DrawSkyDome(viewMatrix);
                            DrawTerrain(Game1.viewMatrix);
                            DrawWater(time);
                            //DrawBillboards(viewMatrix);
                            base.Draw(gameTime);
                            DrawTrees();
                            break;
                        }
                    case Game1.GameStateHorPower:
                        {
                            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 300.0f;
                            GeneratePerlinNoise(time);
                            DrawRefractionMap();
                            DrawReflectionMap();
                            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);
                            //DrawSkyDome(viewMatrix);
                            DrawTerrain(Game1.viewMatrix);
                            DrawWater(time);
                            //DrawBillboards(viewMatrix);
                            base.Draw(gameTime);
                            DrawTrees();
                            break;
                        }

                    default:
                        {
                            float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 300.0f;


                            GeneratePerlinNoise(time);
                            DrawRefractionMap();
                            DrawReflectionMap();
                            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);
                            //DrawSkyDome(viewMatrix);
                            DrawTerrain(Game1.viewMatrix);
                            DrawWater(time);
                            //DrawBillboards(viewMatrix);
                            DrawTrees();
                            base.Draw(gameTime);
                            break;
                        }

                }
            }
            //base.Update(gameTime);
        }
        private void LoadVertices()
        {

            //Texture2D heightMap = Texture2D.FromFile(device,"c:\\heightmap.bmp"); 
            //content.Load<Texture2D>(@"content\textures\map_colorkey"
            Texture2D heightMap = new Texture2D(device,128,128);
            if(Game1.EndofCourseName == null)
                heightMap = Content.Load<Texture2D>(Game1.CoursePath+"Hole"+Convert.ToString(Game1.HoleNum)+"\\heightmap" + Game1.EndofCourseName);
            else
                heightMap = Texture2D.FromFile(device,Game1.CoursePath+"Hole"+Convert.ToString(Game1.HoleNum)+"\\heightmap" + Game1.EndofCourseName);
            LoadHeightData(heightMap);
            VertexMultitextured[] terrainVertices = SetUpTerrainVertices();
            int[] terrainIndices = SetUpTerrainIndices();
            terrainVertices = CalculateNormals(terrainVertices, terrainIndices);
            CopyToTerrainBuffers(terrainVertices, terrainIndices);
            terrainVertexDeclaration = new VertexDeclaration(device, VertexMultitextured.VertexElements);
            
            SetUpWaterVertices();
            waterVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
            Texture2D treeMap = new Texture2D(device, 128, 128);
            if (Game1.EndofCourseName == null)
                treeMap = Content.Load<Texture2D>(Game1.CoursePath + "Hole" + Convert.ToString(Game1.HoleNum) + "//treemap");
            else
                treeMap = Texture2D.FromFile(device,Game1.CoursePath + "Hole" + Convert.ToString(Game1.HoleNum) + "\\treemap" + Game1.EndofCourseName);
            treeList = GenerateTreePositions(treeMap, terrainVertices); CreateBillboardVerticesFromList(treeList);

            fullScreenVertices = SetUpFullscreenVertices();
            fullScreenVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
        }

        private void LoadTextures()
        {

            grassTexture = Content.Load<Texture2D>("grass");
            sandTexture = Content.Load<Texture2D>("sand");
            rockTexture = Content.Load<Texture2D>("rock");
            snowTexture = Content.Load<Texture2D>("snow");
            cloudMap = Content.Load<Texture2D>("cloudMap");
            waterBumpMap = Content.Load<Texture2D>("waterbump");
            treeTexture = Content.Load<Texture2D>("tree");
            //treeMap = Content.Load<Texture2D>("treeMap"); 
            cloudStaticMap = CreateStaticMap(32);
        }

        private void LoadHeightData(Texture2D heightMap)
        {
            float minimumHeight = 0;
            float maximumHeight = 255;

            terrainWidth = heightMap.Width;
            terrainLength = heightMap.Height;

            Color[] heightMapColors = new Color[terrainWidth * terrainLength];
            heightMap.GetData(heightMapColors);

            heightData = new float[terrainWidth, terrainLength];
            for (int x = 0; x < terrainWidth; x++)
                for (int y = 0; y < terrainLength; y++)
                {
                    heightData[x, (terrainWidth - 1) - y] = heightMapColors[x + y * terrainWidth].R;
                    //if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y];
                    //if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y];
                }

            for (int x = 0; x < terrainWidth; x++)
                for (int y = 0; y < terrainLength; y++)
                    heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30.0f;
        }

        private VertexMultitextured[] SetUpTerrainVertices()
        {
            VertexMultitextured[] terrainVertices = new VertexMultitextured[terrainWidth * terrainLength];

            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainLength; y++)
                {
                    terrainVertices[x + y * terrainWidth].Position = new Vector3(x, heightData[x, y], -y);
                    terrainVertices[x + y * terrainWidth].TextureCoordinate.X = (float)x / 30.0f;
                    terrainVertices[x + y * terrainWidth].TextureCoordinate.Y = (float)y / 30.0f;

                    terrainVertices[x + y * terrainWidth].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
                    terrainVertices[x + y * terrainWidth].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
                    terrainVertices[x + y * terrainWidth].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
                    terrainVertices[x + y * terrainWidth].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);

                    float total = terrainVertices[x + y * terrainWidth].TexWeights.X;
                    total += terrainVertices[x + y * terrainWidth].TexWeights.Y;
                    total += terrainVertices[x + y * terrainWidth].TexWeights.Z;
                    total += terrainVertices[x + y * terrainWidth].TexWeights.W;

                    terrainVertices[x + y * terrainWidth].TexWeights.X /= total;
                    terrainVertices[x + y * terrainWidth].TexWeights.Y /= total;
                    terrainVertices[x + y * terrainWidth].TexWeights.Z /= total;
                    terrainVertices[x + y * terrainWidth].TexWeights.W /= total;
                }
            }

            return terrainVertices;
        }

        private int[] SetUpTerrainIndices()
        {
            int[] indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6];
            int counter = 0;
            for (int y = 0; y < terrainLength - 1; y++)
            {
                for (int x = 0; x < terrainWidth - 1; x++)
                {
                    int lowerLeft = x + y * terrainWidth;
                    int lowerRight = (x + 1) + y * terrainWidth;
                    int topLeft = x + (y + 1) * terrainWidth;
                    int topRight = (x + 1) + (y + 1) * terrainWidth;

                    indices[counter++] = topLeft;
                    indices[counter++] = lowerRight;
                    indices[counter++] = lowerLeft;

                    indices[counter++] = topLeft;
                    indices[counter++] = topRight;
                    indices[counter++] = lowerRight;
                }
            }

            return indices;
        }

        private VertexMultitextured[] CalculateNormals(VertexMultitextured[] vertices, int[] indices)
        {
            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal = new Vector3(0, 0, 0);

            for (int i = 0; i < indices.Length / 3; i++)
            {
                int index1 = indices[i * 3];
                int index2 = indices[i * 3 + 1];
                int index3 = indices[i * 3 + 2];

                Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
                Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
                Vector3 normal = Vector3.Cross(side1, side2);

                vertices[index1].Normal += normal;
                vertices[index2].Normal += normal;
                vertices[index3].Normal += normal;
            }

            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal.Normalize();

            return vertices;
        }

        private void CopyToTerrainBuffers(VertexMultitextured[] vertices, int[] indices)
        {
            terrainVertexBuffer = new VertexBuffer(device, vertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
            terrainVertexBuffer.SetData(vertices);

            terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
            terrainIndexBuffer.SetData(indices);
        }

        private void SetUpWaterVertices()
        {
            VertexPositionTexture[] waterVertices = new VertexPositionTexture[6];

            waterVertices[0] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[2] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, -terrainLength), new Vector2(1, 0));
            waterVertices[1] = new VertexPositionTexture(new Vector3(0, waterHeight, -terrainLength), new Vector2(0, 0));

            waterVertices[3] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[5] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, 0), new Vector2(1, 1));
            waterVertices[4] = new VertexPositionTexture(new Vector3(terrainWidth, waterHeight, -terrainLength), new Vector2(1, 0));

            waterVertexBuffer = new VertexBuffer(device, waterVertices.Length * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);
            waterVertexBuffer.SetData(waterVertices);
        }
        
        private void CreateBillboardVerticesFromList(List<Vector3> treeList)
        {
            VertexPositionTexture[] billboardVertices = new VertexPositionTexture[treeList.Count * 6];
            int i = 0;
            foreach (Vector3 currentV3 in treeList)
            {
                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 0));
                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 0));
                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 1));

                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 0));
                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 1));
                billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 1));
            }
            try
            {
                treeVertexBuffer = new VertexBuffer(device, billboardVertices.Length * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);
                treeVertexBuffer.SetData(billboardVertices);
                treeVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
            }
            catch
            { TreesExist = false; }
        }


        public List<Vector3> GenerateTreePositions(Texture2D treeMap, VertexMultitextured[] terrainVertices)
        {
            Color[] treeMapColors = new Color[treeMap.Width * treeMap.Height];
            treeMap.GetData(treeMapColors);

            int[,] noiseData = new int[treeMap.Width, treeMap.Height];
            for (int x = 0; x < treeMap.Width; x++)
                for (int y = 0; y < treeMap.Height; y++)
                    noiseData[x, y] = treeMapColors[x + ((treeMap.Width -1)-y) * treeMap.Height].R;


            List<Vector3> treeList = new List<Vector3>(); 
            Random random = new Random();
            TreeClassList = new List<TreeClass>();
            
            Random Seedmaker= new Random();
            
            AvailablePineTrees = new SimpleTree[numTrees];
            AvailableBirchTrees = new SimpleTree[numTrees];
            AvailableGardenwoodTrees = new SimpleTree[numTrees];
            AvailableWillowTrees = new SimpleTree[numTrees];
            AvailablePineTreesLowPoly = new SimpleTree[numTrees];
            AvailableBirchTreesLowPoly = new SimpleTree[numTrees];
            AvailableGardenwoodTreesLowPoly = new SimpleTree[numTrees];
            AvailableWillowTreesLowPoly = new SimpleTree[numTrees];

            for (int x = 0; x < numTrees; x++)
            {
                AvailableWillowTrees[x] = Treeprofile[0].GenerateSimpleTree(new Random(random.Next(0, 200000)));
                AvailablePineTrees[x] = Treeprofile[1].GenerateSimpleTree(new Random(random.Next(0, 200000)));
                AvailableBirchTrees[x] = Treeprofile[2].GenerateSimpleTree(new Random(random.Next(0, 200000)));
                AvailableGardenwoodTrees[x] = Treeprofile[3].GenerateSimpleTree(new Random(random.Next(0, 200000)));
                AvailableWillowTreesLowPoly[x] = AvailableWillowTrees[x];
                AvailablePineTreesLowPoly[x] = AvailablePineTrees[x];
                AvailableBirchTreesLowPoly[x] = AvailableBirchTrees[x];
                AvailableGardenwoodTreesLowPoly[x] = AvailableGardenwoodTrees[x];
                AvailableWillowTreesLowPoly[x].Trunk = new TreeMesh(device,AvailableWillowTrees[x].Skeleton,4) ;
                AvailablePineTreesLowPoly[x].Trunk = new TreeMesh(device, AvailablePineTrees[x].Skeleton, 4);
                AvailableBirchTreesLowPoly[x].Trunk = new TreeMesh(device, AvailableBirchTrees[x].Skeleton, 4);
                AvailableGardenwoodTreesLowPoly[x].Trunk = new TreeMesh(device, AvailableGardenwoodTrees[x].Skeleton, 4);
                
                                                    
                
            }
            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainLength; y++)
                {
                    float terrainHeight = heightData[x, y];
                    if ((terrainHeight > 5.5) && (terrainHeight < 255))
                    {
                        float flatness = Vector3.Dot(terrainVertices[x + y * terrainWidth].Normal, new Vector3(0, 1, 0));
                        float minFlatness = (float)Math.Cos(MathHelper.ToRadians(60));
                        if (flatness > minFlatness)
                        {
                            float relx = (float)x / (float)terrainWidth;
                            float rely = (float)y / (float)terrainLength;
                                float rand1 = (float)random.Next(2000) / 2000.0f;
                                float rand2 = (float)random.Next(2000) / 2000.0f;
                                Vector3 treePos = new Vector3((float)x - rand1, 0, -(float)y - rand2);
                                treePos.Y = heightData[x, y] -1;
                                if (treeMapColors[x + ((treeMap.Width - 1) - y) * treeMap.Height].G == 255)
                                {
                                    TreeClass temp = new TreeClass();
                                    int TreeSeed = Seedmaker.Next(0, numTrees);
                                    temp.Tree = TreeSeed;
                                    temp.Position = treePos;
                                    temp.TreeType = 1;
                                    temp.Box = new BoundingBox(new Vector3(treePos.X - 1f,treePos.Y,treePos.Z +1f),new Vector3(treePos.X + 1f, treePos.Y + 2.5f,treePos.Z -1f));
                                    TreeClassList.Add(temp);
                                }
                                if (treeMapColors[x + ((treeMap.Width - 1) - y) * treeMap.Height].R == 255)
                                {
                                    TreeClass temp = new TreeClass();
                                    int TreeSeed = Seedmaker.Next(0, numTrees);
                                    temp.Tree = TreeSeed;
                                    temp.Position = treePos;
                                    temp.TreeType = 2;
                                    temp.Box = new BoundingBox(new Vector3(treePos.X - 1f, treePos.Y, treePos.Z + 1f), new Vector3(treePos.X + 1f, treePos.Y + 2.5f, treePos.Z - 1f));
                                    
                                    TreeClassList.Add(temp);
                                }
                                if (treeMapColors[x + ((treeMap.Width - 1) - y) * treeMap.Height].B == 255)
                                {
                                    TreeClass temp = new TreeClass();
                                    int TreeSeed = Seedmaker.Next(0, numTrees);
                                    temp.Tree = TreeSeed;
                                    temp.Position = treePos;
                                    temp.TreeType = 3;
                                    temp.Box = new BoundingBox(new Vector3(treePos.X - 1f, treePos.Y, treePos.Z + 1f), new Vector3(treePos.X + 1f, treePos.Y + 5.5f, treePos.Z - 1f));

                                    TreeClassList.Add(temp);
                                }
                            if (treeMapColors[x + ((treeMap.Width - 1) - y) * treeMap.Height].B == 125)
                            {
                                TreeClass temp = new TreeClass();
                                    int TreeSeed = Seedmaker.Next(0, numTrees);
                                    temp.Tree = TreeSeed;
                                    temp.Position = treePos;
                                    temp.TreeType = 0;
                                    temp.Box = new BoundingBox(new Vector3(treePos.X - 1f, treePos.Y, treePos.Z + 1f), new Vector3(treePos.X + 1f, treePos.Y + 1.5f, treePos.Z - 1f));
                                    TreeClassList.Add(temp);
                            }

                                
                            
                        }
                    }
                }
            }

            return treeList;
        }

        private Texture2D CreateStaticMap(int resolution)
        {
            Random rand = new Random();
            Color[] noisyColors = new Color[resolution * resolution];
            for (int x = 0; x < resolution; x++)
                for (int y = 0; y < resolution; y++)
                    noisyColors[x + y * resolution] = new Color(new Vector3((float)rand.Next(1000) / 1000.0f, 0, 0));

            Texture2D noiseImage = new Texture2D(device, resolution, resolution, 1, TextureUsage.None, SurfaceFormat.Color);
            noiseImage.SetData(noisyColors);
            return noiseImage;
        }

        private VertexPositionTexture[] SetUpFullscreenVertices()
        {
            VertexPositionTexture[] vertices = new VertexPositionTexture[4];

            vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0f), new Vector2(0, 1));
            vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0f), new Vector2(1, 1));
            vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0f), new Vector2(0, 0));
            vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0f), new Vector2(1, 0));

            return vertices;
        }
        private void DrawTerrain(Matrix currentViewMatrix)
        {
            effect.CurrentTechnique = effect.Techniques["MultiTextured"];
            effect.Parameters["xTexture0"].SetValue(sandTexture);
            effect.Parameters["xTexture1"].SetValue(grassTexture);
            effect.Parameters["xTexture2"].SetValue(rockTexture);
            effect.Parameters["xTexture3"].SetValue(snowTexture);

            Matrix worldMatrix = Matrix.Identity;
            effect.Parameters["xWorld"].SetValue(worldMatrix);
            effect.Parameters["xView"].SetValue(currentViewMatrix);
            effect.Parameters["xProjection"].SetValue(CameraProperties.projectionMatrix);

            effect.Parameters["xEnableLighting"].SetValue(true);
            effect.Parameters["xAmbient"].SetValue(0.4f);
            effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f));

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexMultitextured.SizeInBytes);
                device.Indices = terrainIndexBuffer;
                device.VertexDeclaration = terrainVertexDeclaration;

                int noVertices = terrainVertexBuffer.SizeInBytes / VertexMultitextured.SizeInBytes;
                int noTriangles = terrainIndexBuffer.SizeInBytes / sizeof(int) / 3;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles);

                pass.End();
            }
            effect.End();
        }

        private void DrawSkyDome(Matrix currentViewMatrix)
        {
            device.RenderState.DepthBufferWriteEnable = false;

            Matrix[] modelTransforms = new Matrix[skyDome.Bones.Count];
            skyDome.CopyAbsoluteBoneTransformsTo(modelTransforms);

            Matrix wMatrix = Matrix.CreateTranslation(0, -0.3f, 0) * Matrix.CreateScale(100) * Matrix.CreateTranslation(CameraProperties.cameraPosition);
            foreach (ModelMesh mesh in skyDome.Meshes)
            {
                foreach (Effect currentEffect in mesh.Effects)
                {
                    Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;

                     currentEffect.CurrentTechnique = currentEffect.Techniques["SkyDome"];
                     currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
                     currentEffect.Parameters["xView"].SetValue(currentViewMatrix);
                     currentEffect.Parameters["xProjection"].SetValue(CameraProperties.projectionMatrix);
                     currentEffect.Parameters["xTexture"].SetValue(cloudMap);
                 }
                 mesh.Draw();
             }
             device.RenderState.DepthBufferWriteEnable = true;
         }
 
         private Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, bool clipSide)
         {
             planeNormalDirection.Normalize();
             Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
             if (clipSide)
                 planeCoeffs *= -1;

             Matrix worldViewProjection = currentViewMatrix * CameraProperties.projectionMatrix;
             Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
             inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);
 
             planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
             Plane finalPlane = new Plane(planeCoeffs);
 
             return finalPlane;
         }
 
         private void DrawRefractionMap()
         {
             Plane refractionPlane = CreatePlane(waterHeight + 1.5f, new Vector3(0, -1, 0), Game1.viewMatrix, false);
             device.ClipPlanes[0].Plane = refractionPlane;
             device.ClipPlanes[0].IsEnabled = true;
             device.SetRenderTarget(0, refractionRenderTarget);
             device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
             DrawTerrain(Game1.viewMatrix);
             device.ClipPlanes[0].IsEnabled = false;
 
             device.SetRenderTarget(0, null);
             refractionMap = refractionRenderTarget.GetTexture();
         }
 
         private void DrawReflectionMap()
         {
             Plane reflectionPlane = CreatePlane(waterHeight - 0.5f, new Vector3(0, -1, 0), Game1.reflectionViewMatrix, true);
             device.ClipPlanes[0].Plane = reflectionPlane;
             device.ClipPlanes[0].IsEnabled = true;
             device.SetRenderTarget(0, reflectionRenderTarget);
             device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
             DrawSkyDome(Game1.reflectionViewMatrix);
             DrawTerrain(Game1.reflectionViewMatrix);
             
             //DrawBillboards(reflectionViewMatrix);
             device.ClipPlanes[0].IsEnabled = false;
 
             device.SetRenderTarget(0, null);
             reflectionMap = reflectionRenderTarget.GetTexture();
         }
 
         private void DrawWater(float time)
         {
             effect.CurrentTechnique = effect.Techniques["Water"];
             Matrix worldMatrix = Matrix.Identity;
             effect.Parameters["xWorld"].SetValue(worldMatrix);
             effect.Parameters["xView"].SetValue(Game1.viewMatrix);
             effect.Parameters["xReflectionView"].SetValue(Game1.reflectionViewMatrix);
             effect.Parameters["xProjection"].SetValue(CameraProperties.projectionMatrix);
             effect.Parameters["xReflectionMap"].SetValue(reflectionMap);
             effect.Parameters["xRefractionMap"].SetValue(refractionMap);
             effect.Parameters["xWaterBumpMap"].SetValue(waterBumpMap);
             effect.Parameters["xWaveLength"].SetValue(0.1f);
             effect.Parameters["xWaveHeight"].SetValue(0.3f);
             effect.Parameters["xCamPos"].SetValue(CameraProperties.cameraPosition);
             effect.Parameters["xTime"].SetValue(time);
             effect.Parameters["xWindForce"].SetValue(0.002f);
             effect.Parameters["xWindDirection"].SetValue(windDirection);
 
             effect.Begin();
             foreach (EffectPass pass in effect.CurrentTechnique.Passes)
             {
                 pass.Begin();
 
                 device.Vertices[0].SetSource(waterVertexBuffer, 0, VertexPositionTexture.SizeInBytes);
                 device.VertexDeclaration = waterVertexDeclaration;
                 int noVertices = waterVertexBuffer.SizeInBytes / VertexPositionTexture.SizeInBytes;
                 device.DrawPrimitives(PrimitiveType.TriangleList, 0, noVertices / 3);
 
                 pass.End();
             }
             effect.End();
         }
 
         private void DrawBillboards(Matrix currentViewMatrix)
         {
             TreesExist = false;
             if (TreesExist)
             {
                 bbEffect.CurrentTechnique = bbEffect.Techniques["CylBillboard"];
                 bbEffect.Parameters["xWorld"].SetValue(Matrix.Identity);
                 bbEffect.Parameters["xView"].SetValue(currentViewMatrix);
                 bbEffect.Parameters["xProjection"].SetValue(CameraProperties.projectionMatrix);
                 bbEffect.Parameters["xCamPos"].SetValue(CameraProperties.cameraPosition);
                 bbEffect.Parameters["xAllowedRotDir"].SetValue(new Vector3(0, 1, 0));
                 bbEffect.Parameters["xBillboardTexture"].SetValue(treeTexture);

                 bbEffect.Begin();

                 device.Vertices[0].SetSource(treeVertexBuffer, 0, VertexPositionTexture.SizeInBytes);
                 device.VertexDeclaration = treeVertexDeclaration;
                 int noVertices = treeVertexBuffer.SizeInBytes / VertexPositionTexture.SizeInBytes;
                 int noTriangles = noVertices / 3;
                 {
                     device.RenderState.AlphaTestEnable = true;
                     device.RenderState.AlphaFunction = CompareFunction.GreaterEqual;
                     device.RenderState.ReferenceAlpha = 200;

                     bbEffect.CurrentTechnique.Passes[0].Begin();
                     device.DrawPrimitives(PrimitiveType.TriangleList, 0, noTriangles);
                     bbEffect.CurrentTechnique.Passes[0].End();
                 }

                 {
                     device.RenderState.DepthBufferWriteEnable = false;

                     device.RenderState.AlphaBlendEnable = true;
                     device.RenderState.SourceBlend = Blend.SourceAlpha;
                     device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

                     device.RenderState.AlphaTestEnable = true;
                     device.RenderState.AlphaFunction = CompareFunction.Less;
                     device.RenderState.ReferenceAlpha = 200;

                     bbEffect.CurrentTechnique.Passes[0].Begin();
                     device.DrawPrimitives(PrimitiveType.TriangleList, 0, noTriangles);
                     bbEffect.CurrentTechnique.Passes[0].End();
                 }

                 device.RenderState.AlphaBlendEnable = false;
                 device.RenderState.DepthBufferWriteEnable = true;
                 device.RenderState.AlphaTestEnable = false;

                 bbEffect.End();
             }
         }
 
         private void GeneratePerlinNoise(float time)
         {
             device.SetRenderTarget(0, cloudsRenderTarget);
             device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
                         
             effect.CurrentTechnique = effect.Techniques["PerlinNoise"];
             effect.Parameters["xTexture"].SetValue(cloudStaticMap);
             effect.Parameters["xOvercast"].SetValue(1.1f);
             effect.Parameters["xTime"].SetValue(time/1000.0f);
             effect.Begin();
             foreach (EffectPass pass in effect.CurrentTechnique.Passes)
             {
                 pass.Begin();
 
                 device.VertexDeclaration = fullScreenVertexDeclaration;
                 device.DrawUserPrimitives(PrimitiveType.TriangleStrip, fullScreenVertices, 0, 2);
 
                 pass.End();
             }
             effect.End();
 
             device.SetRenderTarget(0, null);
             cloudMap = cloudsRenderTarget.GetTexture();
         }
     
        public float GetTerrainHeight(float fTerX, float fTerY)
        {
            // we first get the height of 4 points of the quad underneath the point
            int xheight;
            int yheight;
            float fTriY0 = 0;
            float fTriY1 =0 ;
            float fTriY2 = 0;
            float fTriY3=0;
            float fHeight;
            float fSqX;
            float fSqY;
            xheight = (int)fTerX;
            yheight = (int)fTerY;
            try
            {
                fTriY0 = (heightData[xheight, yheight]);
                fTriY1 = (heightData[xheight + 1, yheight]);
                fTriY2 = (heightData[xheight, yheight + 1]);
                fTriY3 = (heightData[xheight + 1, yheight + 1]);
            }
            catch
            { }
            // find which of the 2 triangles the point is over (depends on how you render)
            // then take the height at a triangle point
            // and adjust by the slope down right-angle edges * distance along that edge.
            fSqX = fTerX - xheight;
            fSqY = fTerY - yheight;
            if ((fSqX + fSqY) < 1)
            {
                fHeight = fTriY0;
                fHeight += (fTriY1 - fTriY0) * fSqX;
                fHeight += (fTriY2 - fTriY0) * fSqY;
            }
            else
            {
                fHeight = fTriY3;
                fHeight += (fTriY1 - fTriY3) * (1.0f - fSqY);
                fHeight += (fTriY2 - fTriY3) * (1.0f - fSqX);
            }
            return fHeight;
        }
        void LoadHole()
        {
            if (Game1.HoleNum > Game1.CourseHoles)


                Game1.GameState = Game1.GameStateMenu;
            else
            {
                
                Game1.newBasket.graphics = this.GraphicsDevice;
                if (Game1.EndofCourseName == null)
                    Game1.newBasket.LoadImage(Content.Load<Texture2D>(Game1.CoursePath + "Hole" + Convert.ToString(Game1.HoleNum) + "//holemap"));
                else
                    Game1.newBasket.LoadImage(Texture2D.FromFile(device, Game1.CoursePath + "Hole" + Convert.ToString(Game1.HoleNum) + "//holemap" + Game1.EndofCourseName));

                Game1.newBasket.Position.Y = GetTerrainHeight(Game1.newBasket.Position.X, Game1.newBasket.Position.Z * -1) - .1f;
                Game1.newDisc.CheckForTrees();
                Game1.newDisc.ResetDisc();
                Game1.newDisc.Flying = false;
                Game1.newDisc.BasketPosition = Game1.newBasket.Position;
                Game1.newDisc.Position = Game1.newBasket.DiscInitialPosition;
                Game1.newDisc.Position.Y = GetTerrainHeight(Game1.newDisc.Position.X, Game1.newDisc.Position.Z * -1) + 1.4f;


                

            }
            //newDisc.Position.Y = 12;


        }

        void DrawTrees()
        {
            foreach (TreeClass tempTree in TreeClassList)
            {
                if (tempTree.Visible)
                {
                    switch (tempTree.TreeType)
                    {
                        case 0:
                            if (tempTree.UseLowPoly)
                            {
                                AvailableWillowTreesLowPoly[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            else
                            {
                                AvailableWillowTrees[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }

                           // AvailableWillowTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            
                            break;
                        case 1:
                            if (tempTree.UseLowPoly)
                            {
                                AvailablePineTreesLowPoly[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            else
                            {
                                AvailablePineTrees[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            //AvailablePineTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            
                            break;
                        case 2:
                            if (tempTree.UseLowPoly)
                            {
                                AvailableBirchTreesLowPoly[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            else
                            {
                                AvailableBirchTrees[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            //AvailableBirchTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            
                            break;
                        case 3:
                            if (tempTree.UseLowPoly)
                            {
                                AvailableGardenwoodTreesLowPoly[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            else
                            {
                                AvailableGardenwoodTrees[tempTree.Tree].DrawTrunk(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            //AvailableGardenwoodTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            
                            break;
                        default:
                            break;
                    }

                }
            }
            
            foreach (TreeClass tempTree in TreeClassList)
            {
                if (tempTree.Visible)
                {
                    switch (tempTree.TreeType)
                    {

                        case 0:
                            if (tempTree.UseLowPoly)
                            {
                                AvailableWillowTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            break;

                        case 1:
                            if (!tempTree.UseLowPoly)
                            {
                                AvailablePineTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            break;
                        case 2:
                            if (!tempTree.UseLowPoly)
                            {
                                AvailableBirchTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            break;
                        case 3:
                            if (tempTree.UseLowPoly)
                            {
                                
                            }
                            else
                            {
                                AvailableGardenwoodTrees[tempTree.Tree].DrawLeaves(TreeScaleMatrix * Matrix.CreateTranslation(tempTree.Position), Game1.viewMatrix, CameraProperties.projectionMatrix);
                            }
                            break;
                        default:
                            break;
                    }
                }
             
            }
            device.RenderState.DepthBufferEnable = true;
            device.RenderState.AlphaBlendEnable = false;
            device.RenderState.AlphaTestEnable = false;
        }
        


    }
}